use crate::datetime_format;
use crate::i18n_text::UserLocale;
use crate::id_factory::ID_FACTORY;
use crate::model::DataStoreEngine;
use crate::option_datetime_format;
use crate::visit::VisitRequest;
use crate::{hex, i18n, to_sha256_hash, Astr, FieldId, ProjectRsaHelper, RecordId, UserId};
use base64ct::LineEnding;
use chrono::{Local, NaiveDateTime};
use rsa::pkcs1::EncodeRsaPrivateKey;
use rsa::pkcs8::{DecodePublicKey, EncodePublicKey};
use rsa::RsaPublicKey;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fs;
use std::net::IpAddr;
use std::path::Path;
use std::str::FromStr;
use std::sync::{Arc, LazyLock};

pub trait DataSourceTrait {
    fn to_url(&self) -> Astr;
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetaDataStore {
    pub store_engine: DataStoreEngine,
    pub host: IpAddr,
    pub port: usize,
    pub user: Astr,
    pub password: Astr,
}

impl TryFrom<&Value> for MetaDataStore {
    type Error = Astr;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        Ok(Self {
            store_engine: DataStoreEngine::from_str(value["engine"].as_str().unwrap()),
            host: IpAddr::from_str(value["host"].as_str().unwrap()).unwrap(),
            port: value["port"].as_u64().unwrap() as usize,
            user: Arc::from(value["user"].as_str().unwrap()),
            password: Arc::from(value["password"].as_str().unwrap()),
        })
    }
}

impl DataSourceTrait for MetaDataStore {
    fn to_url(&self) -> Astr {
        let url = match self.store_engine {
            #[cfg(feature = "postgres")]
            DataStoreEngine::Postgres => {
                format!(
                    "postgresql://{}:{}@{}:{}/visitdd",
                    self.user,
                    self.password,
                    self.host.to_string(),
                    self.port,
                )
            }
            DataStoreEngine::Mysql => {
                format!(
                    "mysql://{}:{}@{}:{}/visitdd",
                    self.user,
                    self.password,
                    self.host.to_string(),
                    self.port,
                )
            }
        };
        url.into()
    }
}

#[derive(Debug)]
pub struct AppConfig {
    pub server_port: u16,    // 服务端口
    pub db_dir: Astr,        // rocksdb数据文件夹
    pub db_backup_dir: Astr, // rocksdb备份文件夹
    pub upload_dir: Astr,    // 文件上传目录
    pub pub_key: Astr,       //公钥
    pub pri_key: Astr,       //私钥
    pub lang: UserLocale,
    pub ipv6: bool,
    pub meta_store: Option<Arc<MetaDataStore>>,
    pub cluster_name: Astr,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            server_port: 8080,
            db_dir: "./db".into(),
            db_backup_dir: "./db_backup".into(),
            upload_dir: "./upload".into(),
            pub_key: Arc::from(Self::PUBLIC_KEY_STR),
            pri_key: Arc::from(Self::PRIVATE_KEY_STR),
            lang: UserLocale::UlEn,
            ipv6: false,
            meta_store: None,
            cluster_name: "default".into(),
        }
    }
}

impl From<&Value> for AppConfig {
    fn from(value: &Value) -> Self {
        Self {
            server_port: value
                .get("serverPort")
                .map(|j2| j2.as_u64().unwrap())
                .unwrap_or(8443) as u16,
            db_dir: value
                .get("dbDir")
                .map(|j2| j2.as_str().unwrap())
                .unwrap_or("./db")
                .into(),
            db_backup_dir: value
                .get("dbBackupDir")
                .map(|j2| j2.as_str().unwrap())
                .unwrap_or("./db_backup")
                .into(),
            upload_dir: value
                .get("uploadDir")
                .map(|j2| j2.as_str().unwrap())
                .unwrap_or("./upload")
                .into(),
            pub_key: value
                .get("pubKey")
                .map(|j2| j2.as_str().unwrap())
                .map(|s| {
                    let p = Path::new(s);
                    fs::read_to_string(p).unwrap_or(Self::PUBLIC_KEY_STR.to_string())
                })
                .unwrap_or(Self::PUBLIC_KEY_STR.to_string())
                .into(),
            pri_key: value
                .get("priKey")
                .map(|j2| j2.as_str().unwrap())
                .map(|s| {
                    let p = Path::new(s);
                    fs::read_to_string(p).unwrap_or(Self::PRIVATE_KEY_STR.to_string())
                })
                .unwrap_or(Self::PRIVATE_KEY_STR.to_string())
                .into(),
            lang: value["lang"]
                .as_str()
                .map(|x| {
                    if x.eq_ignore_ascii_case("en") {
                        UserLocale::UlEn
                    } else {
                        UserLocale::UlCn
                    }
                })
                .unwrap_or(UserLocale::UlEn),
            ipv6: value["ipv6"].as_bool().unwrap_or(false),
            meta_store: if value["metaStore"].is_object() {
                Some(Arc::new(
                    MetaDataStore::try_from(&value["metaStore"]).unwrap(),
                ))
            } else {
                None
            },
            cluster_name: value
                .get("clusterName")
                .map(|j2| j2.as_str().unwrap())
                .unwrap_or("default")
                .into(),
        }
    }
}

impl AppConfig {
    const PRIVATE_KEY_STR: &'static str = r#"
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNgOG6pJq1JrBa
8R8q+H9NWkefUrg7OnT1/tGCWgYgO8SLyl5RiCH5z3BODk9ArIjCrK13O8dMmc/+
L5ySI+r2HFl1zPbnVI5ZLHzSeRMCSSIqQj7VAdM+QByQmqbKyuTOKd6hq/z1lbuv
+j1ALuH3gbb0QMUmHo6RTuujxGERIc4F/k8Mf6T5NlpceZVnnAIyESJ1gkHCq2C6
jajof111rcl3XaV3dlpkmxjgYhzQEUQwCjeFv1UuCvQcuqh+FHQBLCO4MLZVxzUT
p9MSkgdBYyW5a5zolJ1K0zQLbbyDyTlH1xVzJA8Cux/u6t5HumjMAIEhvPRC8+q4
ubEsg5LZAgMBAAECggEALPgH7TS52ewmaTAcVuXbzYx04ctTHzrSf0YMiI60incp
skFNVGAKt0SKWbpN42Qd5SOuLco5+WwrFH9MrroHX2WiueFpIgPhxmeyxMfTvgtM
25x1bV0h4Zxb49TDrNqrKOYGnrApAYCLep2yq4nWzrdm1poZyhajse5oKAUADKRJ
qW4LgCiKa+Dm3BfhhNxXFMwmNry9YwRJGgasGQLvLjflIFWbJyJLFmxx5wiiFyfG
RhR1mezGviopjuXUO/vnaTiZwZwgGeWonnxjDp38fwEi0+6CfVRNcHnoS3Y4WUUT
HE1rOZMxYgL7ow48w/k2rg1X8uZD8w6A1AoKn+ZXdQKBgQDsD6WGzTr9EdRqV+we
V8i+smrRLmRH9yuY4w0tP6LgVQokDBLf0KYZhPvgL5Ezt+nePc6BqPe7Jme/RSnb
O8+kZt8LXFENaCYEBJzdjtyr4/vQLaRX0bp0Hgx35yteKgDAB7g6zYo5Ba3n8SNz
C8nLmLN1LvdwzxflhuB+WVppUwKBgQDe3HxuhlmMh+j8A4EHqKrVFpfJYXvVa0ne
ZllVckpblTjkO336R9/VU6lIlln3t9CeBL/ZPG+e1TFJbE93D2QmbWfoWqBP58LQ
RLz+kJiURhQ9u08ydK4pluTRXBsHTQetTAkVm8sWWvDgsxxf5SGaaTFFR/hbCEgg
o8Z6mrwRowKBgQDOeYUT2nDtJ+o+tfauN6Q7wD0UCFroY3Z7vKUq3c1uIJPFIdZk
c9DE43+YAKMltIjXvgHhgVRmGtqF/030F2Wwa9ZNzoqKin0t6Ib4d9vmcXwxA6sK
sURACoRnYAibzWtoi5gFvwBWjfQlutKB716rOT5GkjUWM924Anhbq3YcRwKBgCm1
J9BjclTbhp3LZhVx2m7AqK12gf9h8dQeYW+dUQ8ssRX9kEkLtHcvDgpYoQO8Md2w
09qq9dTK30t3arPEfKC4bu7MLx5lKE9j3d7iXru5FcarzFmL/qAn7jDERQarwtkK
1tRlcK+BW30h6BiGQDCuqRrnKjKXLTB2lgmR9cpzAoGAERje3NHUdecEDfekWo4v
xgOHb3SfVe78/SY/hjj0WXP1nIct1eNEjL4PAwACYW0JGwTEhB0YPfvASGaQE8t1
onLJKiKSW7RZjLBO6JDZquocYHTVu+kLFWDucV38lWXKi9pcTHzN2jBVI6kTRc+H
swA63Uxt3YLRVaHFTcXYqf8=
-----END PRIVATE KEY-----"#;
    const PUBLIC_KEY_STR: &'static str = r#"
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzYDhuqSatSawWvEfKvh/
TVpHn1K4Ozp09f7RgloGIDvEi8peUYgh+c9wTg5PQKyIwqytdzvHTJnP/i+ckiPq
9hxZdcz251SOWSx80nkTAkkiKkI+1QHTPkAckJqmysrkzineoav89ZW7r/o9QC7h
94G29EDFJh6OkU7ro8RhESHOBf5PDH+k+TZaXHmVZ5wCMhEidYJBwqtguo2o6H9d
da3Jd12ld3ZaZJsY4GIc0BFEMAo3hb9VLgr0HLqofhR0ASwjuDC2Vcc1E6fTEpIH
QWMluWuc6JSdStM0C228g8k5R9cVcyQPArsf7ureR7pozACBIbz0QvPquLmxLIOS
2QIDAQAB
-----END PUBLIC KEY-----"#;

    fn try_load(app_id: &str) -> Self {
        let path = format!("./{}_config.json", app_id);
        let path = Path::new(&path);
        if !path.exists() || !path.is_file() {
            return Self::default();
        }
        tracing::info!("CONFIG FILE {:?}", path);
        let j = fs::read_to_string(path).expect("failed to read config file");
        let j = serde_json::from_str::<Value>(&j).expect("failed to read config file");
        Self::from(&j)
    }
}

pub static APP_CONFIG: LazyLock<AppConfig> = LazyLock::new(|| AppConfig::try_load("visit_dd"));

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "T", content = "C")]
pub enum VddLogPart {
    LpAction(UserId, Astr),
    LpModel(u64),
    LpField(FieldId),
    LpRecord(RecordId),
    LpFile(u64, Astr),
    LpVisitGroup(u64, u64),
    LpUser(UserId),
    LpFvChange(Astr, Astr), // 值改变记录
    LpRr(u64),              // 记录约束规则
}
///
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VddLog {
    pub id: u64,
    #[serde(with = "datetime_format")]
    pub log_ts: NaiveDateTime,
    pub parts: Vec<VddLogPart>,
}
///
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VddLogDisplay {
    #[serde(with = "datetime_format")]
    pub log_ts: NaiveDateTime,
    pub log: Astr,
}
///
#[macro_export]
macro_rules! alog {
    ($($content:tt)*) => {
        $crate::proj::VddLog::new(&vec![$($content)*])
    };
}

impl VddLog {
    pub fn new(parts: &[VddLogPart]) -> Self {
        Self {
            id: ID_FACTORY.lock().unwrap().lazy_generate(),
            log_ts: Local::now().naive_local(),
            parts: Vec::from(parts),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VddMessage {
    pub id: u64,
    pub sender: UserId,
    pub receiver: UserId,
    pub content: Astr,
    #[serde(with = "datetime_format")]
    pub send_ts: NaiveDateTime,
    #[serde(with = "option_datetime_format")]
    pub read_ts: Option<NaiveDateTime>,
}

impl VddMessage {
    pub fn new(sender: &UserId, receiver: &UserId, message: &str) -> Self {
        Self {
            id: ID_FACTORY.lock().unwrap().lazy_generate(),
            sender: sender.to_owned(),
            receiver: receiver.to_owned(),
            content: Arc::from(message),
            send_ts: Local::now().naive_local(),
            read_ts: None,
        }
    }
    pub fn share_request_handle(sender: UserId, request: &VisitRequest) -> Self {
        Self {
            id: ID_FACTORY.lock().unwrap().lazy_generate(),
            sender: sender.to_owned(),
            receiver: request.request_uid.to_owned(),
            content: i18n!(1008),
            send_ts: Local::now().naive_local(),
            read_ts: None,
        }
    }
}

/// 用户角色
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum VddUserRole {
    UrGuest,
    UrUser,
    UrAdmin,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VddUser {
    pub id: UserId,
    pub phone: Astr,
    pub name: Astr,
    pub password: Astr,
    #[serde(with = "datetime_format")]
    pub register_ts: NaiveDateTime,
    #[serde(with = "option_datetime_format")]
    pub last_login_ts: Option<NaiveDateTime>,
    pub role: VddUserRole,
    pub pri_key: Astr,
    pub pub_key: Astr,
}

impl TryFrom<&Value> for VddUser {
    type Error = Astr;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        let rsa = ProjectRsaHelper::init(&APP_CONFIG.pri_key, &APP_CONFIG.pub_key);
        let pwd = rsa.decode_b64str(value["pwd"].as_str().unwrap()).unwrap();
        let pwd_hash = to_sha256_hash(pwd.as_bytes());
        let mut r = VddUser {
            id: UserId(ID_FACTORY.lock().unwrap().lazy_generate()),
            name: Arc::from(value["phone"].as_str().unwrap()),
            phone: Arc::from(value["phone"].as_str().unwrap()),
            password: pwd_hash,
            register_ts: Local::now().naive_local(),
            last_login_ts: None,
            role: VddUserRole::UrUser,
            pri_key: "".into(),
            pub_key: "".into(),
        };
        r.set_key_pair();
        Ok(r)
    }
}

impl VddUser {
    pub fn set_key_pair(&mut self) {
        let pri_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 1024).unwrap();
        let pub_key = pri_key.clone().to_public_key();
        self.pri_key = hex::encode(pri_key.to_pkcs1_pem(LineEnding::CR).unwrap().as_bytes()).into();
        self.pub_key = hex::encode(
            pub_key
                .to_public_key_pem(LineEnding::CR)
                .unwrap()
                .as_bytes(),
        )
        .into();
    }

    pub fn get_pub_key(&self) -> RsaPublicKey {
        let pubk = hex::decode(self.pub_key.as_bytes()).unwrap();
        let pubk = String::from_utf8(pubk).unwrap();
        RsaPublicKey::from_public_key_pem(&pubk).unwrap()
    }
}
